﻿#include "FITKGraphObjectPostModel.h"

// VTK
#include <vtkProperty.h>
#include <vtkDataSet.h>
#include <vtkMapper.h>
#include <vtkLabelPlacementMapper.h>
#include <vtkFreeTypeLabelRenderStrategy.h>
#include <vtkLabelHierarchy.h>
#include <vtkPointSetToLabelHierarchy.h>
#include <vtkTextProperty.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkCamera.h>
#include <vtkRenderer.h>
#include <vtkExtractSelection.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkVolume.h>
#include <vtkAbstractVolumeMapper.h>
#include <vtkDataSetTriangleFilter.h>
#include <vtkOutlineFilter.h>

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Graph and filter
#include "FITKGraphObjectModelHighlighting.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor2D.h"
#include "FITKGraphCommons.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKDeformFilter.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKPolygonSource.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKMaxMinValueFilter.h"

// Data
#include "FITK_Interface/FITKInterfaceStructuralPost/FITKAbstractStruPostVTK.h"
#include "FITK_Interface/FITKInterfaceStructuralPost/FITKCutViewManager.h"
#include "FITK_Interface/FITKInterfaceStructuralPost/FITKStruPostMessageText.h"

namespace Exchange
{
    FITKGraphObjectPostModel::FITKGraphObjectPostModel(AbaqusData::FITKDataCase* caseData, Interface::FITKAbstractStructuralPostVTK* postData)
        : FITKGraphObjectPostModelBase(caseData, postData)
    {
        if (!postData)
        {
            return;
        }

        // Set the hue type to rainbow.
        setHueType(HueType::Rainbow);

        // Get the rendering output.
        m_postDataOutput = postData->getRenderOutputAlgorithm();

        // Get the display group extractor.
        m_groupExtractor = postData->getDisplayGroupExtractor();

        // The output from the post model.
#ifdef RENDER_VOLUME_TEST
        vtkAlgorithmOutput* outputPort = getPostModelOutput(true);
#else
        vtkAlgorithmOutput* outputPort = getPostModelOutput();
#endif

        if (!outputPort)
        {
            return;
        }

        // Initialize base actor.
        initActors();

        // Connect the output port to the actor.
        if (m_fActor)
        {
            m_fActor->setInputConnection(outputPort);
            m_fActor->update();
        }

#ifdef RENDER_VOLUME_TEST
        // Try to connect the output port to the volume.
        if (m_volFilter)
        {
            m_volFilter->SetInputConnection(outputPort);
        }

        if (m_volOutlineFilter)
        {
            m_volOutlineFilter->SetInputConnection(outputPort);
        }
#endif

        if (m_textActor && postData->getMessageTextData()) {
            Interface::FITKStruPostMessageText* messageText = postData->getMessageTextData();
            connect(messageText, &Interface::FITKStruPostMessageText::sigUpdate, [=]() {
                if (m_textActor) {
                    QString text = messageText->getText();
                    double pos[2] = { 0,0 };
                    messageText->getPos(pos);
                    QColor color = messageText->getColor();
                    int size = messageText->getTextSize();
                    bool isEnable = messageText->getIsEnable();

                    m_textActor->SetInput(text.toUtf8().data());
                    m_textActor->SetPosition(pos[0], pos[1]);
                    m_textActor->GetTextProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
                    m_textActor->GetTextProperty()->SetFontSize(size);
                    m_textActor->SetVisibility(isEnable);
                }
            });
            QString text = messageText->getText();
            double pos[2] = { 0,0 };
            messageText->getPos(pos);
            QColor color = messageText->getColor();
            int size = messageText->getTextSize();
            bool isEnable = messageText->getIsEnable();
            m_textActor->SetInput(text.toUtf8().data());
            m_textActor->SetPosition(pos[0], pos[1]);
            m_textActor->GetTextProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
            m_textActor->GetTextProperty()->SetFontSize(size);
            m_textActor->SetVisibility(isEnable);
        }

        // Create the highlighting object.
        m_graphObjHighlight = new FITKGraphObjectModelHighlighting(caseData, postData);
        m_addinGraphObjList.push_back(m_graphObjHighlight);

        // Initialize the min and max labels and glyph actors.
        initializeLabelsActors();

        // Add input filter.
        m_maxFilter->SetInputConnection(outputPort);
        m_minFilter->SetInputConnection(outputPort);
        m_graphObjHighlight->setModelInputConnection(outputPort);

        // Save the post data information.
        m_graphInfo.Type = PostModelGraph;
        m_graphInfo.PostModelId = postData->getDataObjectID();
    }

    FITKGraphObjectPostModel::~FITKGraphObjectPostModel()
    {
        // Set the point to null.
        m_postDataOutput = nullptr;
        m_groupExtractor = nullptr;
    }

    void FITKGraphObjectPostModel::setEnableDeform(bool isOn)
    {
        Interface::FITKAbstractStructuralPostVTK* postData = dynamic_cast<Interface::FITKAbstractStructuralPostVTK*>(_dataObj);
        if (!postData)
        {
            return;
        }

        // Enable or disable the deformation.
        postData->enableDeform(isOn);
    }

    void FITKGraphObjectPostModel::setPickable(bool isPickable, int dim)
    {
        Q_UNUSED(dim);

        if (m_fActor)
        {
            m_fActor->SetPickable(isPickable);
        }
    }

    bool FITKGraphObjectPostModel::getDataVisibility()
    {
        bool visible = FITKGraphObject3D::getDataVisibility();
        if (!visible)
        {
            return false;
        }

        // Check visible cut.
        bool flag = hasVisibleCutView();
        return !flag;
    }

    void FITKGraphObjectPostModel::update(bool forceUpdate)
    {
        // Update the post model's output.
#ifdef RENDER_VOLUME_TEST
        vtkAlgorithmOutput* outputPort = getPostModelOutput(true);
#else
        vtkAlgorithmOutput* outputPort = getPostModelOutput();
#endif

        if (!outputPort)
        {
            return;
        }

        if (m_fActor)
        {
            m_fActor->setInputConnection(outputPort);
        }

#ifdef RENDER_VOLUME_TEST
        if (m_volFilter)
        {
            m_volFilter->SetInputConnection(outputPort);
        }

        if (m_volOutlineFilter)
        {
            m_volOutlineFilter->SetInputConnection(outputPort);
        }
#endif

        if (m_maxFilter)
        {
            m_maxFilter->SetInputConnection(outputPort);
        }

        if (m_minFilter)
        {
            m_minFilter->SetInputConnection(outputPort);
        }

        // Update the post functions.
        this->Superclass::update(forceUpdate);

        // Update the visibility.
        updateVisibility();
    }

    void FITKGraphObjectPostModel::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!getDataVisibility())
        {
            return;
        }

        if (!m_postDataOutput || !m_graphObjHighlight)
        {
            return;
        }

        m_graphObjHighlight->setModelInputConnection(m_postDataOutput->GetOutputPort());
        m_graphObjHighlight->highlight(color);

        FITKGraphObject3D::highlight();
    }

    void FITKGraphObjectPostModel::disHighlight()
    {
        if (!m_graphObjHighlight || !m_isHighlighting)
        {
            return;
        }

        m_graphObjHighlight->disHighlight();

        FITKGraphObject3D::disHighlight();
    }

    void FITKGraphObjectPostModel::advanceHighlight(QVariant details, QColor color)
    {
        Q_UNUSED(details);

        // If the object is invisible, then return.
        //if (!getDataVisibility())
        //{
        //    return;
        //}

        if (!m_groupExtractor || !m_graphObjHighlight)
        {
            return;
        }

        m_groupExtractor->Update();
        m_graphObjHighlight->setModelInputConnection(m_groupExtractor->GetOutputPort());
        m_graphObjHighlight->highlight(color);

        m_isAdvHighlighting = true;
        // FITKGraphObject3D::advanceHighlight(details, color);
    }

    void FITKGraphObjectPostModel::disAdvanceHighlight()
    {
        if (!m_graphObjHighlight || !m_isAdvHighlighting)
        {
            return;
        }

        m_graphObjHighlight->disHighlight();

        FITKGraphObject3D::disAdvanceHighlight();
    }

    vtkAlgorithmOutput* FITKGraphObjectPostModel::getOutputPort(bool forRendering)
    {
        return getPostModelOutput(!forRendering);
    }

    vtkDataSet* FITKGraphObjectPostModel::getOutput(bool forRendering)
    {
        vtkDataSet* output{ nullptr };
        Interface::FITKAbstractStructuralPostVTK* postData = dynamic_cast<Interface::FITKAbstractStructuralPostVTK*>(_dataObj);
        if (!postData)
        {
            return output;
        }

        vtkAlgorithm* outAlg{ nullptr };
        if (forRendering)
        {
            outAlg = postData->getRenderOutputAlgorithm();
        }
        else
        {
            outAlg = postData->getOutputAlgorithm();
        }

        if (outAlg)
        {
            output = vtkDataSet::SafeDownCast(outAlg->GetOutputDataObject(0));
        }

        return output;
    }

    Interface::FITKMeshFeatureVTK* FITKGraphObjectPostModel::getMeshFeature()
    {
        Interface::FITKAbstractStructuralPostVTK* postData = dynamic_cast<Interface::FITKAbstractStructuralPostVTK*>(_dataObj);
        if (!postData)
        {
            return nullptr;
        }

        return postData->getMeshFeature();
    }

    bool FITKGraphObjectPostModel::hasVisibleCutView()
    {
        Interface::FITKAbstractStructuralPostVTK* postData = dynamic_cast<Interface::FITKAbstractStructuralPostVTK*>(_dataObj);
        if (!postData)
        {
            return false;
        }

        // Check the view cut setting first.
        if (!postData->getShowViewCut())
        {
            return false;
        }

        // Get the cut view manager and all cut views.
        Interface::FITKCutViewManager* cutMgr = postData->getCutViewManager();
        if (!cutMgr)
        {
            return false;
        }

        // Check all cut views' visibility.
        int nCut = cutMgr->getDataCount();
        for (int i = 0; i < nCut; i++)
        {
            Interface::FITKAbstractViewCut* cut = cutMgr->getDataByIndex(i);
            if (!cut)
            {
                continue;
            }

            // Check the visibility.
            if (cut->isVisible())
            {
                return true;
            }
        }

        return false;
    }

    vtkAlgorithmOutput* FITKGraphObjectPostModel::getPostModelOutput(bool isVolume)
    {
        vtkAlgorithmOutput* outputPort{ nullptr };
        Interface::FITKAbstractStructuralPostVTK* postData = dynamic_cast<Interface::FITKAbstractStructuralPostVTK*>(_dataObj);
        if (!postData)
        {
            return outputPort;
        }

        vtkAlgorithm* outAlg{ nullptr };
        if (isVolume)
        {
            outAlg = postData->getOutputAlgorithm();
        }
        else
        {
            outAlg = postData->getRenderOutputAlgorithm();
        }

        if (outAlg)
        {
            return outAlg->GetOutputPort();
        }

        return outputPort;
    }
}   // namespace Exchange